package com.ruoyi.batchinsert;

import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.mapper.SysOperLogMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

// 主控制类
@Service
public class MultiThreadedBatchInsert {

    @Resource
    SysOperLogMapper sysOperLogMapper;

    private final Random sharedRandom = new Random(); // 共享 Random 实例用于多处随机生成

    public void batchInsert() {
        // 1. 模拟生成200万条数据
        List<SysOperLog> dataList = generateTestData(2_000_000);

        // 2. 定义分片大小（例如每1万条一个批次）
        int batchSize = 5000;
        List<List<SysOperLog>> batches = partitionData(dataList, batchSize);

        // 3. 创建固定大小的线程池（线程数建议为CPU核数的1.5-2倍）
        int threadPoolSize = Runtime.getRuntime().availableProcessors() + 4;
        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);

        // 4. 使用CountDownLatch等待所有任务完成
        CountDownLatch latch = new CountDownLatch(batches.size());
        List<Future<Boolean>> futures = new ArrayList<>(batches.size());

        long startTime = System.currentTimeMillis();

        // 5. 提交分片任务到线程池
        for (List<SysOperLog> batch : batches) {
            Future<Boolean> future = executor.submit(new InsertTask(batch, latch, sysOperLogMapper));
            futures.add(future);
        }

        try {
            // 6. 等待所有插入任务完成
            latch.await();
            boolean allSuccess = true;
            for (Future<Boolean> f : futures) {
                try {
                    if (!f.get()) {
                        allSuccess = false;
                    }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                    allSuccess = false;
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("所有数据插入完成，总耗时：" + (endTime - startTime) + " ms");
            if (!allSuccess) {
                System.err.println("部分任务执行失败，请查看详细日志。");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        } finally {
            executor.shutdown();
            try {
                if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                    executor.shutdownNow();
                }
            } catch (InterruptedException ie) {
                executor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }

    // 数据分片方法
    private static <T> List<List<T>> partitionData(List<T> dataList, int batchSize) {
        int totalBatches = (dataList.size() + batchSize - 1) / batchSize;
        List<List<T>> batches = new ArrayList<>(totalBatches);
        for (int i = 0; i < dataList.size(); i += batchSize) {
            int end = Math.min(i + batchSize, dataList.size());
            batches.add(dataList.subList(i, end));
        }
        return batches;
    }

    // 模拟生成测试数据
    private List<SysOperLog> generateTestData(int count) {
        List<SysOperLog> dataList = new ArrayList<>(count);
        String[] methods = {"GET", "POST", "PUT", "DELETE"};
        String[] moduleNames = {"订单服务", "用户中心", "支付网关", "库存系统"};

        for (int i = 0; i < count; i++) {
            SysOperLog log = new SysOperLog();

            log.setTitle(moduleNames[sharedRandom.nextInt(moduleNames.length)] + "_" + randomString("", 5, sharedRandom));
            log.setBusinessType(sharedRandom.nextInt(10)); // 0-9之间的业务类型

            log.setMethod(randomString("com.example.service.Method", 3, sharedRandom));
            log.setRequestMethod(methods[sharedRandom.nextInt(methods.length)]);
            log.setOperatorType(sharedRandom.nextInt(3)); // 0-2之间的操作类别

            log.setOperName(randomString("user_", 6, sharedRandom));
            log.setDeptName(randomString("部门_", 4, sharedRandom));
            log.setOperUrl("/api/" + randomString("", 10, sharedRandom));
            log.setOperIp("192.168." + sharedRandom.nextInt(256) + "." + sharedRandom.nextInt(256));
            log.setOperLocation(randomString("位置_", 6, sharedRandom));

            log.setOperParam("{\"param" + i + "\":\"value" + i + "\"}");
            log.setJsonResult("{\"result" + i + "\":\"success" + i + "\"}");

            int status = sharedRandom.nextInt(2); // 0或1
            log.setStatus(status);
            if (status == 1) {
                log.setErrorMsg(randomString("错误信息_", 20, sharedRandom));
            }

            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DAY_OF_YEAR, -sharedRandom.nextInt(365)); // 过去一年内的随机时间
            log.setOperTime(calendar.getTime());
            log.setCostTime((long) sharedRandom.nextInt(5000)); // 0-5000毫秒的耗时
            dataList.add(log);
        }
        return dataList;
    }

    /**
     * 生成随机字符串
     *
     * @param prefix 前缀
     * @param length 长度
     * @param random Random 实例
     * @return 随机字符串
     */
    private static String randomString(String prefix, int length, Random random) {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder sb = new StringBuilder(prefix);
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }
}
